package com.allwees.data.analysis.stat.vo;

import com.allwees.bs.core.model.stat.EStatType;
import com.allwees.bs.core.model.stat.EStatValueType;
import com.allwees.bs.core.modelbase.constant.EPlatform;
import com.allwees.core.common.util.DateUtil;
import com.allwees.data.analysis.stat.EStatMode;
import com.allwees.data.analysis.stat.req.QueryStatReq;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.math.util.MathUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 *
 *
 * @author yangylsky
 * @since 2020-04-14
 *
 */
@Data
@EqualsAndHashCode(callSuper=false)
@Accessors(chain = true)
public abstract class StatVo<SD extends StatDetailVo> extends StatInfo {
	protected Date dateFrom;

	protected Date dateTo;

    protected BigDecimal valuePrev = BigDecimal.ZERO;

    /**
     * 较上一个统计周期的增量
     */
    protected Float increment = 0.0f;

    protected String incrementDesc;

    protected String statTypeSummary;

    protected List<SD> details = new ArrayList<>();

    @SuppressWarnings("unchecked")
	public static <SD extends StatDetailVo, S extends StatVo<SD>> S newStat(EStatMode statMode) {
    	switch(statMode) {
			case DAILY:
				return (S) new DailyStatVo();
			case MONTHLY:
				return (S) new MonthlyStatVo();
			default:
				return null;
		}
    }

    @SuppressWarnings("unchecked")
	public static <SD extends StatDetailVo> SD newStatDetail(EStatMode statMode, Date date) {
    	switch(statMode) {
			case DAILY:
				return (SD) new DailyStatDetailVo().setDate(date);
			case MONTHLY:
				return (SD) new MonthlyStatDetailVo().setDate(date);
			default:
				return null;
		}
    }

    public static <SD extends StatDetailVo, S extends StatVo<SD>, Q extends QueryStatReq> S newEmptyStat(Q req) {
    	S stat = newStat(req.statMode());
    	stat.setDateFrom(req.getDateFrom())
    			.setDateTo(req.getDateTo())
    			.complementDetails(req)
    			.setStatType(req.getStatType())
    			.setPlatform(req.getPlatform());
    	return stat;
    }

    protected abstract int interval();

    @SuppressWarnings("unchecked")
	public <S extends StatVo<SD>> S setValuePrev(BigDecimal valuePrev) {
    	if(valuePrev != null) {
        	this.valuePrev = valuePrev.setScale(4, RoundingMode.HALF_UP);
    	}
    	return (S) this;
    }

    public <S extends StatVo<SD>> S setValuePrev(Double valuePrev) {
    	return setValuePrev(BigDecimal.valueOf(valuePrev));
    }

    public EStatValueType getValueType() {
    	return statType.getValueType();
    }

    public String getValueDesc() {
    	switch(statType.getValueType()) {
			case INT:
			case LAST1:
				return String.valueOf(value.intValue());
			case DOUBLE1:
			case AVG:
				return value.setScale(1, RoundingMode.HALF_UP).toString();
			case DOUBLE2:
				return value.setScale(2, RoundingMode.HALF_UP).toString();
			case PERCENT:
				return value.multiply(BigDecimal.valueOf(100)).setScale(1, RoundingMode.HALF_UP) + "%";
			default:
				return "";
		}
    }

    public StatVo<SD> setIncrement(Float increment) {
    	this.increment = increment == null ? 0.0f : MathUtils.round(increment, 3);
    	incrementDesc = BigDecimal.valueOf(this.increment * 100).setScale(1, RoundingMode.HALF_UP) + "%";
    	return this;
    }

    @SuppressWarnings("unchecked")
	@Override
	public <S extends StatInfo> S setStatType(EStatType statType) {
    	super.setStatType(statType);
    	this.statTypeSummary = statType.getSummary();
    	return (S) this;
    }

    public StatVo<SD> caculateIncrement(StatVo<SD> prevStat) {
    	return caculateIncrement(prevStat.getValue());
    }

    public StatVo<SD> caculateIncrement(Double valuePrev) {
    	return caculateIncrement(BigDecimal.valueOf(valuePrev));
    }

    public StatVo<SD> caculateIncrement(BigDecimal valuePrev) {
		setValuePrev(valuePrev);
    	return setIncrement(caculateIncrement(valuePrev, this.value));
    }

	public static Float caculateIncrement(BigDecimal valuePrev, BigDecimal value) {
		if(valuePrev != null && valuePrev.doubleValue() == 0) {
			return value.doubleValue() == 0 ? 0.0f : value.doubleValue() > 0 ? 9.99f : 9.99f;
		} else {
			return (float) (value.doubleValue() / valuePrev.doubleValue() - 1);
		}
	}

	/**
     * 根据req起止时间补全details
     *
     * @param req
     * @return
     */
    @SuppressWarnings("unchecked")
	public <S extends StatVo<SD>, Q extends QueryStatReq> S complement(Q req) {
		setDateFrom(req.getDateFrom()).setDateTo(req.getDateTo()).setStatType(req.getStatType());
		if(StringUtils.isNotBlank(name)) {
			setStatTypeDesc(name);
		}
		if(statTypeDesc.equals(statType.getDesc()) && StringUtils.isNotBlank(bsKey)) {
			setStatTypeDesc(bsKey);
		}
    	return (S) this;
    }

	/**
     * 根据req起止时间补全details
     *
     * @param req
     * @return
     */
    @SuppressWarnings("unchecked")
	public <S extends StatVo<SD>, Q extends QueryStatReq> S complementDetails(Q req) {
    	if(details == null) {
    		details = new ArrayList<>();
        	setDetails(details);
    	}
		details.forEach(detail -> detail.setStatType(req.getStatType()));
    	for(int i = 0; i < req.interval(); i++) {
    		Date date = req.offsetDateFrom(i);
    		if(!_hasDetail(date)) {
				SD detail = StatVo.newStatDetail(req.statMode(), date);
    			detail.setStatType(req.getStatType()).setPlatform(req.getPlatform());
        		details.add(i, detail);
    		}
    	}
    	return (S) this;
    }

    private boolean _hasDetail(Date date) {
    	for(SD detail : details) {
			if(DateUtil.isSameDay(detail.getDate(), date)) {
				return true;
			}
		}
    	return false;
    }

	/**
	 * 根据EStatType.valueType处理特殊value的值
	 *
	 * @param <S>
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <S extends StatVo<SD>> S fixValue() {
		if(statType.getValueType() == EStatValueType.AVG) {
			//设置value为平均值
			setValue(value.divide(BigDecimal.valueOf(interval()), BigDecimal.ROUND_HALF_UP));
			//设置prevValue为平均值
			setValuePrev(valuePrev.divide(BigDecimal.valueOf(interval()), BigDecimal.ROUND_HALF_UP));
		} else if(statType.getValueType() == EStatValueType.LAST1) {
			if(CollectionUtils.isNotEmpty(details)) {
				//设置value为最近一个日期值
				StatDetailVo lastDetail = details.get(details.size() - 1);
				setValue(lastDetail.getValue());
				//设置prevValue为平均值
				setValuePrev(valuePrev.divide(BigDecimal.valueOf(interval()), BigDecimal.ROUND_HALF_UP));
				/**
				 * 因为LAST1类型的值，每种platform的值会sum叠加，所以当platform==null时，需要还原为单份的数值（除去TESTCASE类型）
				 */
				if(platform == null) {
					BigDecimal duplicateNum = BigDecimal.valueOf(EPlatform.values().length - 1);
					setValue(value.divide(duplicateNum, BigDecimal.ROUND_HALF_UP));
					setValuePrev(valuePrev.divide(duplicateNum, BigDecimal.ROUND_HALF_UP));
					details.forEach(detail -> detail.setValue(detail.getValue().divide(duplicateNum, BigDecimal.ROUND_HALF_UP)));
				}
			}
		}
	    return (S) this;
	}
}
